import Foundation
import ImCommon
import SQLite3

// 服务器
class Server {
    private var mDatabase: OpaquePointer?                   // 数据库
    private var mOnlineUsers: Dictionary<String, Client>    // 在线用户列表
    private var mSocket: Int32                              // 套接字
    private var mThread: Thread?                            // 线程
    
    /**
     * 构造方法。
     */
    public init() {
        // 初始化成员
        mOnlineUsers = Dictionary<String, Client>()
        mSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
        
        // 创建数据库
        self.createDatabase()
    }

    /**
     * 析构方法。
     */
    deinit {
        // 关闭套接字
        Darwin.close(mSocket)
        
        // 关闭数据库
        sqlite3_close_v2(mDatabase)

        // 清空在线用户列表
        for onlineUser in mOnlineUsers {
            onlineUser.value.close()
        }
        mOnlineUsers.removeAll()
    }
    
    /**
     * 创建数据库。
     */
    private func createDatabase() {
        // 打开数据库
        let databasePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! + "/" + Const.DB_NAME
        sqlite3_open(databasePath, &mDatabase)
        
        // 创建表
        /* 用户表 */
        var sql = String(format: "CREATE TABLE IF NOT EXISTS %@(%@ TEXT PRIMARY KEY,%@ TEXT,%@ INTEGER)",
            Const.TABLE_USERS, Const.FIELD_NAME, Const.FIELD_PASSWORD, Const.FIELD_ICON_INDEX)
        sqlite3_exec(mDatabase, sql, nil, nil, nil)

        /* 好友表 */
        sql = String(format: "CREATE TABLE IF NOT EXISTS %@(%@ TEXT,%@ TEXT)",
            Const.TABLE_FRIENDS, Const.FIELD_OWNER, Const.FIELD_FRIEND_NAME)
        sqlite3_exec(mDatabase, sql, nil, nil, nil)

        /* 离线聊天表 */
        sql = String(format: "CREATE TABLE IF NOT EXISTS %@(%@ TEXT,%@ TEXT,%@ TEXT,%@ TEXT)",
            Const.TABLE_OFFLINE_CHAT, Const.FIELD_DST_USER, Const.FIELD_SRC_USER, Const.FIELD_CONTENT, Const.FIELD_TIME)
        sqlite3_exec(mDatabase, sql, nil, nil, nil)

        /* 离线添加好友表 */
        sql = String(format: "CREATE TABLE IF NOT EXISTS %@(%@ TEXT,%@ TEXT,%@ INTEGER)",
            Const.TABLE_OFFLINE_ADD_FRIEND, Const.FIELD_SRC_USER, Const.FIELD_DST_USER, Const.FIELD_RESULT)
        sqlite3_exec(mDatabase, sql, nil, nil, nil)
    }

    /**
     * 处理添加好友请求。
     * @param request   添加好友请求
     * @param client    客户端
     */
    private func handleAddFriendRequest(_ request: AddFriendRequest, client: Client) {
        var query: OpaquePointer?
        var sql: String!
        
        // 检查目标用户是否存在
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@'", Const.TABLE_USERS, Const.FIELD_NAME, request.mDstUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        if sqlite3_step(query) == SQLITE_DONE {
            // 查无此人，通知源用户
            let result = AddFriendResult(0, dstUsername: request.mDstUsername, resultCode: .USER_NOT_FOUND, srcUsername: "")
            client.sendData(result)
            sqlite3_finalize(query)
            return
        }
        sqlite3_finalize(query)
        
        // 检查是否已经是好友了
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@' AND %@='%@'", Const.TABLE_FRIENDS, Const.FIELD_OWNER, request.mSrcUsername, Const.FIELD_FRIEND_NAME, request.mDstUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        if sqlite3_step(query) != SQLITE_DONE {
            // 已经是好友了，通知源用户
            let result = AddFriendResult(0, dstUsername: request.mDstUsername, resultCode: .ALREADY_BE_FRIENDS, srcUsername: "")
            client.sendData(result)
            return
        }
        sqlite3_finalize(query)
        
        // 如果目标用户不在线，则将请求信息插入到数据库中
        let friendUser = mOnlineUsers[request.mDstUsername]
        if friendUser == nil {
            sql = String(format: "INSERT INTO %@(%@,%@) VALUES('%@','%@')", Const.TABLE_OFFLINE_ADD_FRIEND,
                Const.FIELD_SRC_USER, Const.FIELD_DST_USER, request.mSrcUsername, request.mDstUsername)
            sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
            sqlite3_step(query)
            sqlite3_finalize(query)
            return
        }
        
        // 向目标用户发送请求
        friendUser!.sendData(request)
    }
    
    /**
     * 处理添加好友结果。
     * @param result    添加好友结果
     */
    private func handleAddFriendResult(_ result: AddFriendResult) {
        var query: OpaquePointer?
        var sql: String!
        
        // 如果结果是同意，则将相关信息写到好友表中
        if result.mResultCode == .ADD_FRIEND_ALLOW {
            sql = String(format: "INSERT INTO %@ VALUES('%@','%@')", Const.TABLE_FRIENDS, result.mSrcUsername, result.mDstUsername)
            sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
            sqlite3_step(query)
            sqlite3_finalize(query)

            sql = String(format: "INSERT INTO %@ VALUES('%@','%@')", Const.TABLE_FRIENDS, result.mDstUsername, result.mSrcUsername)
            sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
            sqlite3_step(query)
            sqlite3_finalize(query)
        }

        // 如果目标用户不在线，则将结果信息插入到数据库中
        let friendUser = mOnlineUsers[result.mSrcUsername]
        if friendUser == nil {
            sql = String(format: "INSERT INTO %@ VALUES('%@','%@','%d')",
                Const.TABLE_OFFLINE_ADD_FRIEND, result.mSrcUsername, result.mDstUsername, result.mResultCode.rawValue)
            sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
            sqlite3_step(query)
            sqlite3_finalize(query)
            return
        }
        
        // 向目标用户发结果
        friendUser!.sendData(result)
    }
    
    /**
     * 处理聊天。
     * @param chatInfo  聊天信息
     */
    private func handleChat(_ chatInfo: ChatInfo) {
        // 处理群发消息
        if chatInfo.mDstUsername == ImProtocol.MULTI_CHAT {
            for i in mOnlineUsers {
                // 跳过自己，向其它用户发送聊天信息
                if i.key != chatInfo.mSrcUsername {
                    i.value.sendData(chatInfo)
                }
            }
            return
        }

        // 如果目标用户不在线，则将聊天信息插入到数据库中
        let friendUser = mOnlineUsers[chatInfo.mDstUsername]
        if friendUser == nil {
            var query: OpaquePointer?
            let sql = String(format: "INSERT INTO %@ VALUES('%@','%@','%@','%@')",
                Const.TABLE_OFFLINE_CHAT, chatInfo.mDstUsername, chatInfo.mSrcUsername, chatInfo.mContent, chatInfo.mTime)
            sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
            sqlite3_step(query)
            sqlite3_finalize(query)
            return
        }
        
        // 发送给目标用户
        friendUser!.sendData(chatInfo)
    }
    
    /**
     * 处理离线请求。
     * @param request    离线请求
     */
    private func handleLogoff(_ request: LogoffRequest) {
        // 将该用户从在线列表移除
        mOnlineUsers.removeValue(forKey: request.mUsername)
        
        // 广播该用户的离线请求
        for user in mOnlineUsers {
            user.value.sendData(request)
        }
    }
    
    /**
     * 处理登录请求。
     * @param request   登录请求
     * @param client    客户端
     */
    private func handleLogon(_ request: LogonRequest, client: Client) {
        let logonResult = LogonResult()
        var query: OpaquePointer?
        var sql: String!

        // 检查该用户是否已经登录
        let onlineUser = mOnlineUsers[request.mUserInfo.mUsername]
        if onlineUser != nil {
            // 已经登录了
            logonResult.mResultCode = .USER_ALREADY_LOGON
            client.sendData(logonResult)
            return
        }
        
        // 从数据库中查找该用户
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@'", Const.TABLE_USERS, Const.FIELD_NAME, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        if sqlite3_step(query) == SQLITE_DONE {
            // 查无此人
            sqlite3_finalize(query)
            logonResult.mResultCode = .USER_NOT_FOUND
            client.sendData(logonResult)
            return
        }

        // 比对密码
        let password = String(cString: sqlite3_column_text(query, 1))
        if password != request.mUserInfo.mPassword {
            // 密码不正确
            sqlite3_finalize(query)
            logonResult.mResultCode = .PASSWORD_WRONG
            client.sendData(logonResult)
            return
        }

        // 登录成功，获取该用户的相关信息
        logonResult.mResultCode = .LOGON_SUCCEEDED
        logonResult.mIconIndex = Int(sqlite3_column_int(query, 2))
        sqlite3_finalize(query)

        // 获取该用户的好友列表
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@'", Const.TABLE_FRIENDS, Const.FIELD_OWNER, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)

        var friendCount = 0
        let friendList = NSMutableArray()
        var friendName: String?
        var iconIndex = -1
        var iconIndexQuery: OpaquePointer?
        var isOnline = false
        let onlineList = NSMutableArray()
        
        while sqlite3_step(query) == SQLITE_ROW {
            friendName = String(cString: sqlite3_column_text(query, 1))

            sql = String(format: "SELECT %@ FROM %@ WHERE %@='%@'", Const.FIELD_ICON_INDEX, Const.TABLE_USERS, Const.FIELD_NAME, friendName!)
            sqlite3_prepare_v2(mDatabase, sql, -1, &iconIndexQuery, nil)
            sqlite3_step(iconIndexQuery)
            iconIndex = Int(sqlite3_column_int(iconIndexQuery, 0))
            sqlite3_finalize(iconIndexQuery)
            
            let userInfo = UserInfo(iconIndex, password: "", username: friendName!)
            friendList.add(userInfo)

            let friendUser = mOnlineUsers[friendName!]
            isOnline = friendUser != nil
            onlineList.add(isOnline)

            // 如果好友在线，则向好友发送通知
            if isOnline {
                let friendLogon = FriendLogon(request.mUserInfo.mUsername)
                friendUser!.sendData(friendLogon)
            }
            
            friendCount += 1
        }
        sqlite3_finalize(query)
        logonResult.mFriendCount = friendCount
        logonResult.mFriends = friendList
        logonResult.mFriendsOnline = onlineList

        // 发送登录结果
        client.sendData(logonResult)

        // 查询是否有和该用户相关的离线添加好友请求，有则发送
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@' AND %@ IS NULL",
            Const.TABLE_OFFLINE_ADD_FRIEND, Const.FIELD_DST_USER, request.mUserInfo.mUsername, Const.FIELD_RESULT)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        while sqlite3_step(query) == SQLITE_ROW {
            let srcUsername = String(cString: sqlite3_column_text(query, 0))
            sql = String(format: "SELECT %@ FROM %@ WHERE %@='%@'", Const.FIELD_ICON_INDEX, Const.TABLE_USERS, Const.FIELD_NAME, srcUsername)
            sqlite3_prepare_v2(mDatabase, sql, -1, &iconIndexQuery, nil)
            sqlite3_step(iconIndexQuery)

            let iconIndex = Int(sqlite3_column_int(iconIndexQuery, 0))
            let addFriendRequest = AddFriendRequest(request.mUserInfo.mUsername, srcIconIndex: iconIndex, srcUsername: srcUsername)
            client.sendData(addFriendRequest)
            sqlite3_finalize(iconIndexQuery)
        }
        sqlite3_finalize(query)
        
        // 删除数据库中的离线添加好友请求
        sql = String(format: "DELETE FROM %@ WHERE %@='%@'", Const.TABLE_OFFLINE_ADD_FRIEND, Const.FIELD_DST_USER, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        sqlite3_step(query)
        sqlite3_finalize(query)

        // 查询是否有和该用户相关的离线添加好友结果，有则发送
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@' AND %@ IS NOT NULL",
            Const.TABLE_OFFLINE_ADD_FRIEND, Const.FIELD_SRC_USER, request.mUserInfo.mUsername, Const.FIELD_RESULT)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        var dstUsername: String!
        while sqlite3_step(query) == SQLITE_ROW {
            dstUsername = String(cString: sqlite3_column_text(query, 1))
            let resultCode = ResultCode(rawValue: Int(sqlite3_column_int(query, 2)))
            let addFriendResult = AddFriendResult(-1, dstUsername: dstUsername, resultCode: resultCode!, srcUsername: request.mUserInfo.mUsername)
            client.sendData(addFriendResult)
        }
        sqlite3_finalize(query)

        // 删除数据库中的离线添加好友结果
        sql = String(format: "DELETE FROM %@ WHERE %@='%@'", Const.TABLE_OFFLINE_ADD_FRIEND, Const.FIELD_SRC_USER, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        sqlite3_step(query)
        sqlite3_finalize(query)

        // 查询是否有和该用户相关的离线聊天消息，有则发送
        sql = String(format: "SELECT * FROM %@ WHERE %@='%@'", Const.TABLE_OFFLINE_CHAT, Const.FIELD_DST_USER, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        var srcUsername: String!
        while sqlite3_step(query) == SQLITE_ROW {
            srcUsername = String(cString: sqlite3_column_text(query, 1))
            sql = String(format: "SELECT %@ FROM %@ WHERE %@='%@'",
                Const.FIELD_ICON_INDEX, Const.TABLE_USERS, Const.FIELD_NAME, srcUsername)
            sqlite3_prepare_v2(mDatabase, sql, -1, &iconIndexQuery, nil)
            sqlite3_step(iconIndexQuery)
            let srcIconIndex = Int(sqlite3_column_int(iconIndexQuery, 0))
            sqlite3_finalize(iconIndexQuery)
            let content = String(cString: sqlite3_column_text(query, 2))
            let time = String(cString: sqlite3_column_text(query, 3))
            let chatInfo = ChatInfo(content, dstUsername: request.mUserInfo.mUsername, srcIconIndex: srcIconIndex, srcUsername: srcUsername, time: time)
            client.sendData(chatInfo)
        }
        sqlite3_finalize(query)

        // 删除数据库中的离线聊天信息
        sql = String(format: "DELETE FROM %@ WHERE %@='%@'", Const.TABLE_OFFLINE_CHAT, Const.FIELD_DST_USER, request.mUserInfo.mUsername)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        sqlite3_step(query)
        sqlite3_finalize(query)
        
        // 将该用户加入在线列表
        mOnlineUsers[request.mUserInfo.mUsername] = client
    }
    
    /**
     * 处理注册请求。
     * @param request   注册请求
     * @param client    客户端
     */
    private func handleReg(_ request: RegRequest, client: Client) {
        let regResult = RegResult()
        var query: OpaquePointer?
        
        // 将该用户信息插入到数据库中
        let sql = String(format: "INSERT INTO %@ VALUES('%@','%@','%d')", Const.TABLE_USERS,
            request.mUserInfo.mUsername, request.mUserInfo.mPassword, request.mUserInfo.mIconIndex)
        sqlite3_prepare_v2(mDatabase, sql, -1, &query, nil)
        if sqlite3_step(query) != SQLITE_DONE {
            // 该用户已存在
            sqlite3_finalize(query)
            regResult.mResultCode = .USER_EXISTS
            client.sendData(regResult)
            return
        }
        sqlite3_finalize(query)

        // 发送结果
        regResult.mResultCode = .REG_SUCCEEDED
        client.sendData(regResult)

        // 将该用户加入到在线列表中
        mOnlineUsers[request.mUserInfo.mUsername] = client
    }

    /**
     * 处理客户端发送来的数据。
     * @param pro       协议
     * @param client    客户端
     */
    public func onGetMessageFromClient(_ client: Client, pro: ImProtocol) {
        switch pro.mType {
            case .ADD_FRIEND_REQUEST:
                self.handleAddFriendRequest(pro as! AddFriendRequest, client: client)
            case .ADD_FRIEND_RESULT:
                self.handleAddFriendResult(pro as! AddFriendResult)
            case .CHAT:
                self.handleChat(pro as! ChatInfo)
            case .LOGOFF:
                self.handleLogoff(pro as! LogoffRequest)
            case .LOGON:
                self.handleLogon(pro as! LogonRequest, client: client)
            case .REG:
                self.handleReg(pro as! RegRequest, client: client)
            default:
                break
        }
    }

    /**
     * 服务器线程。
     */
    @objc private func server_thread() {
        var addr = sockaddr()
        var len = socklen_t(MemoryLayout<sockaddr>.size)
        var clientSocket: Int32
        
        while true {
            clientSocket = accept(mSocket, &addr, &len)
            _ = Client(self, sock: clientSocket)
        }
    }
    
    /**
     * 开启服务。
     */
    public func start() -> Bool {
        // 绑定 IP 地址和端口号
        var addr = sockaddr_in()
        memset(&addr, 0, MemoryLayout<sockaddr_in>.size)
        addr.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)
        addr.sin_family = sa_family_t(AF_INET)
        addr.sin_port = _OSSwapInt16(ImProtocol.SERVER_PORT)
        addr.sin_addr.s_addr = inet_addr(ImProtocol.SERVER_IP)
        var serverAddr = sockaddr()
        memcpy(&serverAddr, &addr, MemoryLayout<sockaddr_in>.size)
        var result = bind(mSocket, &serverAddr, socklen_t(MemoryLayout<sockaddr>.size))
        if result != 0 {
            return false
        }
        
        // 侦听
        result = listen(mSocket, SOMAXCONN)
        if result != 0 {
            return false
        }
        
        // 开启接收数据的线程
        mThread = Thread(target: self, selector: #selector(server_thread), object: nil)
        mThread?.start()
        
        return true
    }
}
